import requests
import arrow
from src.config.config import app_config
from alibabacloud_dingtalk.oauth2_1_0.client import Client as Oauth2Client
from alibabacloud_dingtalk.oauth2_1_0.models import GetAccessTokenRequest

from alibabacloud_dingtalk.calendar_1_0.client import Client as CalendarClient
from alibabacloud_dingtalk.calendar_1_0.models import CreateEventHeaders, \
    CreateEventRequest, CreateEventRequestStart, CreateEventRequestEnd, \
    DeleteEventHeaders, DeleteEventRequest

from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_util import models as util_models

from src.db.ticket import TicketDao

from src.util.log import log

__assess_token = {}


def get_dingding_oauth2_client() -> Oauth2Client:
    config = open_api_models.Config()
    config.protocol = 'https'
    config.region_id = 'central'
    return Oauth2Client(config)


def get_dingding_calendar_client() -> CalendarClient:
    config = open_api_models.Config()
    config.protocol = 'https'
    config.region_id = 'central'
    return CalendarClient(config)


def refresh_assess_token() -> str:
    oauth2client = get_dingding_oauth2_client()
    try:
        request = GetAccessTokenRequest(
            app_key=app_config.dd_app_key,
            app_secret=app_config.dd_app_secret
        )
        response = oauth2client.get_access_token(request)
        result = response.to_map()
        if result["statusCode"] == 200:
            return result["body"]["accessToken"]
        raise Exception("get access token failed")
    except Exception as err:
        raise err


def get_access_token() -> str:
    now = int(arrow.now().timestamp())
    token_expiration_date = 1800
    if "expiration_date" not in __assess_token or now > __assess_token["expiration_date"] + token_expiration_date:
        __assess_token["expiration_date"] = now
        __assess_token["token"] = refresh_assess_token()
        log("从网络中获取access_token")
    else:
        log(f"从缓存中获取access_token")
    return __assess_token["token"]


def get_userid(access_token: str) -> str:
    response = requests.post(f"https://oapi.dingtalk.com/topapi/v2/user/getbymobile?access_token={access_token}", data={
        "mobile": app_config.mobile
    })
    json = response.json()
    if json["errcode"] == 0:
        return json["result"]["userid"]
    raise Exception("get user_id failed")


def get_union_id(access_token: str, userid: str) -> str:
    response = requests.post(f"https://oapi.dingtalk.com/topapi/v2/user/get?access_token={access_token}", data={
        "userid": userid
    })
    if response.json()["errcode"] == 0:
        return response.json()["result"]["unionid"]
    raise Exception("get union_id failed")


def create_event(title: str, description: str,
                 start_datetime: arrow.Arrow,
                 advance_remind_minutes: int) -> str:
    access_token = get_access_token()
    log(f"获取access_token成功：{access_token}")
    userid = get_userid(access_token=access_token)
    log(f"获取userid成功：{userid}")
    unionid = get_union_id(access_token, userid)
    log(f"获取unionid成功：{unionid}")
    headers = CreateEventHeaders()
    headers.x_acs_dingtalk_access_token = access_token
    start = CreateEventRequestStart(
        date_time=str(start_datetime.shift(minutes=-1 * advance_remind_minutes)),
        time_zone='Asia/Shanghai'
    )
    end = CreateEventRequestEnd(
        date_time=str(start_datetime.shift(minutes=-1 * advance_remind_minutes + 5)),
        time_zone='Asia/Shanghai'
    )
    log(f"日程开始：{start.date_time}")
    log(f"日程结束：{end.date_time}")
    request = CreateEventRequest(
        summary=title,
        description=description,
        start=start,
        end=end,
        is_all_day=False,
    )
    try:
        client = get_dingding_calendar_client()
        response = client.create_event_with_options(
            unionid, "primary", request, headers, util_models.RuntimeOptions(),
        ).to_map()
        if response["statusCode"] == 200:
            return response["body"]["id"]
        raise "创建日程失败"
    except Exception as err:
        raise err


def del_dingding_task(event_id: str) -> bool:
    access_token = get_access_token()
    log(f"获取access_token成功：{access_token}")
    userid = get_userid(access_token=access_token)
    log(f"获取userid成功：{userid}")
    unionid = get_union_id(access_token, userid)
    log(f"获取unionid成功：{unionid}")

    delete_event_headers = DeleteEventHeaders()
    delete_event_headers.x_acs_dingtalk_access_token = access_token
    delete_event_request = DeleteEventRequest(
        push_notification=False
    )
    try:
        client = get_dingding_calendar_client()
        response = client.delete_event_with_options(unionid, 'primary', event_id,
                                                    delete_event_request, delete_event_headers,
                                                    util_models.RuntimeOptions()).to_map()
        return response["statusCode"] == 200
    except Exception as err:
        raise err


def run_dingding_service():
    try:
        results = TicketDao.all_not_handle_tickets()
        log(f"找到{len(results)}条行程待处理")
        for ticket in results:
            advance_remind_minutes = 60
            if ticket.start_place.startswith("北京南站"):
                advance_remind_minutes = 70
            elif ticket.start_place.startswith("天津站"):
                advance_remind_minutes = 40

            event_id = create_event(
                title=f"{ticket.train_number}|{ticket.start_place}-{ticket.end_place}|{ticket.time}",
                description=f"""
                {ticket.seat}
                {ticket.ticket_entrance}
                {ticket.date}
                """,
                start_datetime=arrow.get(ticket.date + " " + ticket.time, "YYYY年MM月DD日 HH:mm",
                                         tzinfo="Asia/Shanghai"),
                advance_remind_minutes=advance_remind_minutes
            )
            if event_id:
                TicketDao.create_event_success(ticket.order_no, event_id)
                log(f"{ticket.order_no}:行程创建成功")
    except Exception as err:
        log(err)
